#pragma once

#include <rocksdb/slice.h>

#include <string>

#include "slash/coding.h"

/*
 * |  <Key Size>  |      <Key>      | <Version> |  <Score>  |      <Member> | 4
 * Bytes      key size Bytes    4 Bytes     8 Bytes    member size Bytes
 */

class ZSetsScoreKey {
 public:
  ZSetsScoreKey(const rocksdb::Slice &key, int32_t version, double score,
                const rocksdb::Slice &member)
      : start_(nullptr),
        key_(key),
        version_(version),
        score_(score),
        member_(member) {}

  ~ZSetsScoreKey() {
    if (start_ != space_) {
      delete[] start_;
    }
  }

  const rocksdb::Slice Encode() {
    size_t needed =
        key_.size() + member_.size() + sizeof(int32_t) * 2 + sizeof(uint64_t);
    char *dst_ptr = nullptr;

    if (needed <= sizeof(space_)) {
      dst_ptr = space_;
    } else {
      dst_ptr = new char[needed];
      if (start_ != space_) {
        delete[] start_;
      }
    }

    start_ = dst_ptr;
    slash::EncodeFixed32(dst_ptr, key_.size());
    dst_ptr += sizeof(int32_t);
    ::memcpy(dst_ptr, key_.data(), key_.size());
    dst_ptr += key_.size();

    slash::EncodeFixed32(dst_ptr, version_);
    dst_ptr += sizeof(int32_t);

    const void *addr_score = reinterpret_cast<const void *>(&score_);
    slash::EncodeFixed64(dst_ptr,
                         *reinterpret_cast<const uint64_t *>(addr_score));

    ::memcpy(dst_ptr, member_.data(), member_.size());
    return rocksdb::Slice(start_, needed);
  }

 private:
  char space_[200];
  char *start_;
  rocksdb::Slice key_;
  int32_t version_;
  double score_;
  rocksdb::Slice member_;
};

class ParsedZSetsScoreKey {
 public:
  ParsedZSetsScoreKey(const std::string *key) {
    const char *data_ptr = key->data();
    int32_t key_len = slash::DecodeFixed32(data_ptr);
    data_ptr += sizeof(int32_t);
    key_ = rocksdb::Slice(data_ptr, key_len);
    version_ = slash::DecodeFixed32(data_ptr);
    data_ptr += sizeof(int32_t);

    uint64_t tmp = slash::DecodeFixed64(data_ptr);
    const void *ptr_tmp = reinterpret_cast<const void *>(&tmp);
    score_ = *reinterpret_cast<const double *>(ptr_tmp);

    data_ptr += sizeof(uint64_t);
    member_ =
        rocksdb::Slice(data_ptr, key->size() - key_len - 2 * sizeof(int32_t) -
                                     sizeof(uint64_t));
  }

  ParsedZSetsScoreKey(const rocksdb::Slice &key) {
    const char *data_ptr = key.data();
    int32_t key_len = slash::DecodeFixed32(data_ptr);
    data_ptr += sizeof(int32_t);
    key_ = rocksdb::Slice(data_ptr, key_len);
    version_ = slash::DecodeFixed32(data_ptr);
    data_ptr += sizeof(int32_t);

    uint64_t tmp = slash::DecodeFixed64(data_ptr);
    const void *ptr_tmp = reinterpret_cast<const void *>(&tmp);
    score_ = *reinterpret_cast<const double *>(ptr_tmp);

    data_ptr += sizeof(uint64_t);
    member_ =
        rocksdb::Slice(data_ptr, key.size() - key_len - 2 * sizeof(int32_t) -
                                     sizeof(uint64_t));
  }

  rocksdb::Slice key() { return key_; }

  int32_t version() const { return version_; }

  double score() const { return score_; }

  rocksdb::Slice member() { return member_; }

 private:
  rocksdb::Slice key_;
  int32_t version_;
  double score_;
  rocksdb::Slice member_;
};
